﻿using System;

namespace LibMQTT.TCPServices
{
    using System.Timers;
    using System.Text;
    using Architecture;
    using ProtocalIMPL;
    using System.Runtime.Caching;
    using System.Net.Sockets;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;

    /// <summary>
    /// 在设计这个程式的时候 对于OSI7层模型并不算`精通`
    /// 犯了一个致命错误: 将守护进程设计成UDP的套接字服务 但很快我发现一个问题：
    /// UDP必须在编译器生成可执行文件之前就知道对方的IP地址(Udp传输依赖于Endpoint)
    /// 这就很麻烦了 : UDP也许只适合用来做一些 局域网内的传输|对实时性要求不高的程式
    /// 所以很快我就用TCP重新实现了这个程式 很快就实现了2个核心数据结构 1.消息队列 2.用户链表
    /// 而这2个内核数据结构用UDP几乎是很难实现的: 
    ///		1.Udp不调用Accept()函数 不知道动态IP是多少 没法模拟用户连接 不像TCP是基于连接的 
    ///		2.Udp的包传输非常快 但传输是无序且简单的 大文件传输 `无序包`几乎是不可能做到|可靠传输|的
    /// 基于这2个原因 最后只能用TCP重写 MqLotService守护进程
    /// 当然重写过程也非常简单 TCP就比UDP 多了2个函数 1.Listen() 2.Accept()
    /// </summary>
    public sealed partial class MqLotService : MqLotBase, INMbsDuplexLegacy
    {
        private readonly int workInterVal = -1;

        // 进程启动 先开启 传输线程(Sock接收) 再开启推送线程(Sock写入)
        public MqLotService(int port, int PingTime) : base(port)
        {
            // Tcp绑定+监听
            workInterVal = PingTime;// 434423432

            Task.Factory.StartNew(RunDaemonLegacy);
            Task.Factory.StartNew(DigPushPipe);
            Task.Factory.StartNew(SelfProbe);
        }

        #region 实现接口 INMbsDuplexLegacy
        // 循环接收[MQTT-V3.1]可变报文
        public void RunDaemonLegacy()
        {
            // 生命周期挂一个定时器
            ReleaseWatchDog(workInterVal);

            EndPoint LoEP = new IPEndPoint(IPAddress.Any, _Port);
            _Listener.Bind(LoEP);
            _Listener.Listen(20); // 挂起20个 客户端并发连接

            while (true)
            {
                /* Tcp工作流 通不停歇地`传输线程` */
                Socket forkChild = _Listener.Accept();
                while (true)
                {
                    /* Loop: ~[Read / Write] */
                    bool forkStatus = AcceptMqttPayload(forkChild);
                    if (forkStatus)
                        continue;
                    else
                        break;
                }
                EndMqttConnection(forkChild);
            }
            /* 位置空间 - 黑洞【进程到这里就永远结束了】*/
        }

        // TCP打洞 模拟1~N推送 一个线程
        public void DigPushPipe()
        {

        }
        
        // TCP自走炮 用一个线程模拟Client客户端 守护线程|进程自动之后 自己ping自己
        private void SelfProbe()
        {
            Thread.Sleep(236);
            MqLotBase PingMachine = new MqLotBase(12589);
            PingMachine._Listener.Connect(IPAddress.Loopback, _Port);
            if (PingMachine._Listener.Connected)
            {
                var bInfo = MqLotBase._BlockCoder.GetBytes("Conn - 发起连接");
                PingMachine._Listener.MqttQoSSend(IBMComPacketType.Connect, bInfo);

                var LoopPing = new System.Timers.Timer();
                LoopPing.AutoReset = true;
                LoopPing.Interval = 1000 * workInterVal;
                LoopPing.Elapsed += (sender, e) => {
                    string ProbeInfo = @"<临江仙 - 滚滚长江东逝水> 
滚滚长江东逝水 浪花淘尽英雄
是非成败转头空 青山依旧在 几度夕阳红
白发渔樵江楮上 惯看秋月春风
一壶浊酒喜相逢 古今多少事 都付笑谈中
--- By.杨慎 明朝";
                    var dataInfo = _BlockCoder.GetBytes(ProbeInfo);
                    // 发送 `MQTT.Connect 连接报文`
                    PingMachine._Listener.MqttQoSSend(IBMComPacketType.SubScribe, dataInfo);
                };
                LoopPing.Start();
            }
        }

        public int PageSize { get { return MqLotBase._BlockSize; } }
        public Encoding Unicode { get { return MqLotBase._BlockCoder; } }
        #endregion

        /// 断臂求生: 关闭`单一线程`绑定的套接字
        private void EndMqttConnection(Socket forkChild)
        {
            var sInfo = "断臂求生: 关闭`单一线程`绑定的套接字";
            Console.WriteLine(sInfo);
            forkChild.Shutdown(SocketShutdown.Receive);
            forkChild.MqttQoSSend(IBMComPacketType.DisConnect, _BlockCoder.GetBytes(sInfo));
            forkChild.Close();
        }

        /// <summary>
        /// MqTT服务层内核函数 加载有效载荷报文
        /// </summary>
        /// <param name="forkChild">文件描述符 - TCP流式</param>
        /// <returns>读取成功则返回True 一切断开连接返回False</returns>
        private bool AcceptMqttPayload(Socket forkChild)
        {
            IBMComPacketType type;
            // 处理报文日志
            List<byte> result = forkChild.MqttQoSRecv(out type);

            string innerMsg = string.Empty;
            switch (type)
            {
                /* 并非所有 接受的消息类型 都有 可变报文+Payload有效载荷 */
                case IBMComPacketType.PingReq:
                    innerMsg = "[PingRequest 心跳请求包] - 跳过字段解析";
                    Console.WriteLine(innerMsg);
                    break;
                case IBMComPacketType.PingResp:
                    innerMsg = "[PinResponse 心跳响应] - 跳过字段解析";
                    Console.WriteLine(innerMsg);
                    break;
                case IBMComPacketType.DisConnect:
                    innerMsg = "[DisConnect 断开连接] - 跳过字段解析";
                    Console.WriteLine(innerMsg);
                    break;
                case IBMComPacketType.ConnAck:
                    innerMsg = "[ConnAck 连接成功] - 不再重复发送";
                    Console.WriteLine(innerMsg);
                    break;
                case IBMComPacketType.Connect:
                case IBMComPacketType.Publish:
                case IBMComPacketType.SubScribe:
                    // 关键步骤 1.解析Mqtt报文
                    var VP = new MqPayloadVariable(result);
                    // 关键步骤 2.处理内核数据结构 `消息队列`
                    MessageTimeLine.Value.Enqueue(VP);
                    break;
            }
            return true;
        }

        /// <summary>
        /// 用MemoryCache处理用户Session缓存
        /// </summary>
        private void HandleSession(string SessionKey, ushort ExpireSeconds)
        {
            ObjectCache cachePool = MemoryCache.Default;

            var userToken = cachePool[SessionKey] as string;
            if (string.IsNullOrEmpty(userToken))
            {
                var policy = new CacheItemPolicy
                {
                    AbsoluteExpiration = DateTime.Now.AddSeconds(ExpireSeconds),
                };
                cachePool.Set("嗯哼~", SessionKey, policy);
            }
        }
        /// <summary>
        /// Server端 内部使用的定时清理内存程序
        /// </summary>
        /// <param name="internalVal"></param>
        private void ReleaseWatchDog(int internalVal)
        {

        }
    }
}
